/**
 * 
 */
package Phase2;

import java.io.Serializable;

import org.junit.Assert;

//import java.util.ArrayList;

/**
 * @author rpadilla
 *
 */
public class UpState implements NodeState, Serializable
{
	private static final long serialVersionUID = 1L;
	private static  NodeState singleton;
	//id of lowest down pointer
	private Node high;
	//id of parent Id + 1
	private int low;
	
	
	/**
	 * private constructor
	 */
	private UpState()
	{
		
	}
	/**
	 * getter of singleton
	 */
	public static NodeState getState()
	{
		if(singleton == null)
		{
			singleton = new UpState();
		}
		return singleton;
	}
	
	@Override
	public boolean lockState(Node callingNode, boolean rootAccess) throws InterruptedException
	{
		boolean allSatisfied = false;
		Node upPointer = null;
		
		boolean[] satisfied = new boolean[3];
		allSatisfied = callingNode.acquireAccessPriviledge(rootAccess);
		satisfied[0] = allSatisfied;
		
		if(allSatisfied)
		{
			upPointer = callingNode.getUpPointers(rootAccess).getMax();
			if(upPointer != null)
			{
				allSatisfied = upPointer.acquireAccessPriviledge(rootAccess);
				satisfied[1] = allSatisfied;
			}
			else
			{
				allSatisfied = false;
				satisfied[1] = false;
			}
		}
		else
		{
			allSatisfied = false;
		}
		
		if(!allSatisfied)
		{
			if(satisfied[0])
			{
				callingNode.releaseAccessPriviledge();
			}
			if(satisfied[1])
			{
				upPointer.releaseAccessPriviledge();
			}
		}
		
		return allSatisfied;
	}

	/**
	 * @param callingNode - node to goRight() from
	 * @see Phase2.NodeState#goRight(Phase2.Node)
	 * @pre callingNode.state instanceOf(DownState)
	 * @post return instance of NodeOrRange to goRight to
	 */
	@Override
	public NodeOrRange goRight(Node callingNode, boolean rootAccess) throws InterruptedException 
	{
		Node upPointer = callingNode.getUpPointers(rootAccess).getMax();
		
		high = callingNode;
		NodeList up = callingNode.getUpPointers(rootAccess);
		if(up.size() > 0)
		{
			low = upPointer.getParent(rootAccess).getId(rootAccess) + 1;
		}
		else
		{
			callingNode.releaseAccessPriviledge();
			upPointer.releaseAccessPriviledge();
			//should NEVER be reached if code is correct
			throw new IllegalStateException("goRight() UpState method called when" 
					+ " there are no Up Pointers.");
		}

		//need to return a range from low to high not a Node
		callingNode.releaseAccessPriviledge();
		upPointer.releaseAccessPriviledge();
		return new InsertionRange(low, high);
	}

	/**
	 * checks if the node's is correctly assigned an upState
	 * @param node checks this node
	 * @return whether it is in an upState or not
	 * @pre node != null, node is of the correct state
	 * @post return true if it is the correct state, otherwise false
	 */
	public boolean assertState(Node node) throws InterruptedException
	{
		boolean rootAccess = true;
		
		Assert.assertTrue(node != null);
		
		Assert.assertTrue(node.getDownPointers(rootAccess).size() == 0);
		
		Assert.assertTrue(node.getUpPointers(rootAccess).size() > 0);
		
		Assert.assertTrue(node.getNeighbours(rootAccess).size() > 0);		
		
		return true;
	}
	
	/**
	 * Returns a string representation of the state
	 * @return "UpState"
	 * @pre None
	 * @post return "UpState"
	 */
	public String toString()
	{
		return "UpState";
	}

	
}

